package be.scond.cartpool.API;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;

import be.scond.cartpool.exceptions.ClosedShoppingListFoundException;
import be.scond.cartpool.exceptions.GroupDoesNotExistException;
import be.scond.cartpool.exceptions.NoShoppingListFoundException;
import be.scond.cartpool.exceptions.OpenShoppingListFoundException;
import be.scond.cartpool.exceptions.OrderDoesNotExistException;
import be.scond.cartpool.exceptions.UserNotFoundException;
import be.scond.cartpool.model.Group;
import be.scond.cartpool.model.Order;
import be.scond.cartpool.model.ShoppingList;
import be.scond.cartpool.model.User;
import be.scond.utils.PMF;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public class ShoppingListApi {
	
	public static ShoppingList createNewShoppingList(String groupName) throws GroupDoesNotExistException, OpenShoppingListFoundException {
		//check for logged in user
		// check for a non closed shopping list on the group. continue when non exists.
		// create a shoppingList on the group
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Group group = GroupApi.getGroup(pm, groupName);
			try {
				group.getOpenShoppingList();
				throw new OpenShoppingListFoundException();				
			} catch (NoShoppingListFoundException e) {
				// OK we can continue creating a new one
				ShoppingList newList = new ShoppingList();
				newList.setDate(new Date());
				
				// a new shoppingList needs a List<Order>
				List<Order> orders = new ArrayList<Order>();
				newList.setOrders(orders);
				
				group.getShoppingLists().add(newList);
				pm.close();
				
				return newList;
			}
			
		} catch (GroupDoesNotExistException e) {
			throw e;
		}
	}
	
	public static Order createOrderOnOldShoppingList(String email, String groupName, String product, int quantity, BigDecimal price, String shoppingListKey, String description) throws UserNotFoundException, NoShoppingListFoundException {
		PersistenceManager pm = PMF.get().getPersistenceManager();		
		try {
			User user = UserApi.getUser(pm, email, groupName);
			//Group group = user.getGroup();
			
			try {
				Key k = KeyFactory.stringToKey(shoppingListKey);
				ShoppingList shoppingList = pm.getObjectById(ShoppingList.class, k);					
				boolean isShopClosed = shoppingList.isClosed();
				
				Order newOrder = new Order();
				newOrder.setPrice(price);
				newOrder.setProduct(product);
				newOrder.setDescription(description);
				newOrder.setQuantity(quantity);
				newOrder.setUserKey(user.getKey());
				newOrder.setUserName(user.getName());
				shoppingList.getOrders().add(newOrder);
				pm.close();	
				
				
				if(isShopClosed){
					PersistenceManager pm2 = PMF.get().getPersistenceManager();
					try {
						reCalculateShoppingListsAfterChange(GroupApi.getGroup(pm2, groupName));	
						pm2.close();
					} catch (GroupDoesNotExistException e) {
						// TODO: handle exception
					}				
				}
				
				
				return newOrder;
			} catch (JDOObjectNotFoundException e) {
				throw new NoShoppingListFoundException();
			}
			
		} catch (UserNotFoundException e) {
			throw e;
		}
	}
	
	public static Order createOrder(String email, String groupName, String product, int quantity, BigDecimal price, String description) throws UserNotFoundException, NoShoppingListFoundException {
		PersistenceManager pm = PMF.get().getPersistenceManager();		
		try {
			User user = UserApi.getUser(pm, email, groupName);
			Group group = user.getGroup();
			
			try {
				ShoppingList shoppingList = group.getOpenShoppingList();				
				
				Order newOrder = new Order();
				newOrder.setPrice(price);
				newOrder.setProduct(product);
				newOrder.setDescription(description);
				newOrder.setQuantity(quantity);
				newOrder.setUserKey(user.getKey());
				newOrder.setUserName(user.getName());
				shoppingList.getOrders().add(newOrder);
				pm.close();				
				return newOrder;
			} catch (NoShoppingListFoundException e) {
				throw e;
			}
			
		} catch (UserNotFoundException e) {
			throw e;
		}
	}
	
	public static Order updateOrderDescription(String keyString, String description) throws OrderDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			Order o = pm.getObjectById(Order.class, k);				
			o.setDescription(description);
			pm.close();
			return o;
		} catch (JDOObjectNotFoundException e){
			throw new OrderDoesNotExistException();
		}
	}	
	public static Order updateOrder(String keyString, String product) throws OrderDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			Order o = pm.getObjectById(Order.class, k);				
			o.setProduct(product);
			pm.close();
			return o;
		} catch (JDOObjectNotFoundException e){
			throw new OrderDoesNotExistException();
		}
	}	
	public static Order updateOrder(String keyString, int quantity) throws OrderDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			Order o = pm.getObjectById(Order.class, k);		
			o.setQuantity(quantity);
			ShoppingList shopList = o.getShoppingList();
			boolean isShopClosed = shopList.isClosed();
			Group group = shopList.getGroup();
			String groupName = group.getName();
			pm.close();		
			
			if(isShopClosed){
				PersistenceManager pm2 = PMF.get().getPersistenceManager();
				try {
					reCalculateShoppingListsAfterChange(GroupApi.getGroup(pm2, groupName));	
					pm2.close();
				} catch (GroupDoesNotExistException e) {
					// TODO: handle exception
				}				
			}
			
			return o;
		} catch (JDOObjectNotFoundException e){
			throw new OrderDoesNotExistException();
		}
	}
	public static Order updateOrder(String keyString, BigDecimal price) throws OrderDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			Order o = pm.getObjectById(Order.class, k);				
			o.setPrice(price);
			ShoppingList shopList = o.getShoppingList();
			boolean isShopClosed = shopList.isClosed();
			Group group = shopList.getGroup();
			String groupName = group.getName();
			pm.close();
			
			if(isShopClosed){
				PersistenceManager pm2 = PMF.get().getPersistenceManager();
				try {
					reCalculateShoppingListsAfterChange(GroupApi.getGroup(pm2, groupName));	
					pm2.close();
				} catch (GroupDoesNotExistException e) {
					// TODO: handle exception
				}				
			}
			
			return o;
		} catch (JDOObjectNotFoundException e){
			throw new OrderDoesNotExistException();
		}
	}
	
	public static void deleteOrder(String keyString) throws OrderDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			Order o = pm.getObjectById(Order.class, k);
			ShoppingList shopList = o.getShoppingList();
			boolean isShopClosed = shopList.isClosed();
			Group group = shopList.getGroup();
			String groupName = group.getName();
			pm.deletePersistent(o);
        	pm.close();
        	
			if(isShopClosed){
				PersistenceManager pm2 = PMF.get().getPersistenceManager();
				try {
					reCalculateShoppingListsAfterChange(GroupApi.getGroup(pm2, groupName));	
					pm2.close();
				} catch (GroupDoesNotExistException e) {
					// TODO: handle exception
				}				
			}
			
        } catch (JDOObjectNotFoundException e){
        	throw new OrderDoesNotExistException();
        }
	}
	
	public static List<Order> getOrders(String keyString) throws NoShoppingListFoundException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Key k = KeyFactory.stringToKey(keyString);
		try{
			ShoppingList shopList = pm.getObjectById(ShoppingList.class, k);				
			List<Order> orders = shopList.getOrders();
			for (Order order : orders) {
				@SuppressWarnings("unused")
				String p = order.toXmlString();
			}
			pm.close();				
			return orders;
		} catch (JDOObjectNotFoundException e){
        	throw new NoShoppingListFoundException();
        }
	}
	

	public static List<ShoppingList> getShoppingLists(String groupName, int fromRange, int toRange) throws GroupDoesNotExistException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Group g = GroupApi.getGroup(pm, groupName);
			List<ShoppingList> allShoppingLists = g.getShoppingLists();
			List<ShoppingList> returnedShoppingLists = new ArrayList<ShoppingList>();
			
			if(toRange > allShoppingLists.size()) {
				toRange = allShoppingLists.size();
			}			
			
			if(fromRange < toRange){				
				for (int i = fromRange; i < toRange; i++) {
					ShoppingList shoppingList = allShoppingLists.get(i);
					returnedShoppingLists.add(shoppingList);
				}				
			}
			
			pm.close();
			return returnedShoppingLists;
		} catch (GroupDoesNotExistException e) {
			throw e;
		}		
	}
	
	private static void reCalculateShoppingListsAfterChange(Group group){
		List<User> userList = group.getUsers();
		List<ShoppingList> allShoppingLists = group.getShoppingLists();
		
		for (User user : userList) {
			user.setSaldo(BigDecimal.ZERO);
		}
		
		for (ShoppingList shoppingList : allShoppingLists) {
			if(shoppingList.isClosed()){
				BigDecimal listTotal = BigDecimal.ZERO;
				for (User user : userList) {	
					BigDecimal userTotal = user.getSaldo();

					List<Order> orders = shoppingList.getOrders();
					for (Order order : orders) {
						if(order.getUserKey().equals(user.getKey())){
							BigDecimal orderSum = order.getPrice().multiply(new BigDecimal(order.getQuantity()));
							listTotal = listTotal.add(orderSum);
							userTotal = userTotal.subtract(orderSum);
						}
					}										
					user.setSaldo(userTotal);
				}
				
				User userWhoCloses = null;
				for (User user : userList) {
					if(user.getKey().equals(shoppingList.getClosedByUserKey())){
						userWhoCloses = user;
						break;
					}
				}

				userWhoCloses.setSaldo(userWhoCloses.getSaldo().add(listTotal));
				shoppingList.setPayedAmount(listTotal);
			}
		}
	}
	
	
	public static void closeShoppingList(String shoppingListKey, String email, String groupName) throws NoShoppingListFoundException, UserNotFoundException, ClosedShoppingListFoundException {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		try {
			Key k = KeyFactory.stringToKey(shoppingListKey);
			ShoppingList shoppingList = pm.getObjectById(ShoppingList.class, k);
			
			if(shoppingList.isClosed()){
				throw new ClosedShoppingListFoundException();
			}else{				
				// check whether a total can be calculated for each order.
				List<Order> orders = shoppingList.getOrders();			
				BigDecimal totalOrdered = BigDecimal.ZERO;
				User userWhoCloses = null;
				List<User> usersWhoOrdered = new ArrayList<User>();
				for (Order order : orders) {				
					BigDecimal orderTotal = order.getPrice().multiply(new BigDecimal(order.getQuantity()));
					totalOrdered = totalOrdered.add(orderTotal).round(MathContext.DECIMAL32);

					User user = pm.getObjectById(User.class, order.getUserKey());
					if(user.getEmail().equals(email)){
						userWhoCloses = user;
					}
					
					// see if a user already showed up in this for loop for an order
					if(user.getTempSaldo() == null){
						usersWhoOrdered.add(user); //add it to a list
						user.setTempSaldo(user.getSaldo().add(BigDecimal.ZERO));
					}
					
					// Subtract the amount of each order of the users' saldo.
					user.setTempSaldo(user.getTempSaldo().subtract(orderTotal));
				}
				
				for (User user : usersWhoOrdered){
					user.setSaldo(user.getTempSaldo().round(MathContext.DECIMAL32));
				}
				
				if(userWhoCloses == null){
					try {
						userWhoCloses = UserApi.getUser(pm, email, groupName);
						userWhoCloses.setTempSaldo(userWhoCloses.getSaldo());					
					} catch (UserNotFoundException e) {
						throw e;
					}
				}
				// add the total amount of all orders to the saldo of the person closing the shoppingList.
				BigDecimal userWhoClosesTempSaldo = userWhoCloses.getTempSaldo();
				userWhoClosesTempSaldo = userWhoClosesTempSaldo.add(totalOrdered);
				userWhoCloses.setSaldo(userWhoClosesTempSaldo.round(MathContext.DECIMAL32));
				shoppingList.setPayedAmount(totalOrdered);
				

				// close the list
				shoppingList.setClosedByUserKey(userWhoCloses.getKey());
				shoppingList.setClosedByUserName(userWhoCloses.getName());
				for (User user : usersWhoOrdered){
					user.setTempSaldo(null);
				}
				userWhoCloses.setTempSaldo(null);
				shoppingList.setClosed(true);
				pm.close();				
			}
		} catch (JDOObjectNotFoundException e) {
			throw new NoShoppingListFoundException();
		} finally {
           /* if (tx.isActive()) {
                tx.rollback();
            }*/
        }
	}
}
